<!--
 * @Author: mayijun
 * @Date: 2022-07-18 18:37:55
 * @LastEditors: mayijun
 * @LastEditTime: 2022-08-11 14:44:57
-->
<script lang="ts">
export default {
  name: 'ContentEdit'
};
</script>
<script lang="ts" setup>
import API from '@/services';
import { nextTick, onMounted, PropType, reactive, ref, watch } from 'vue';
import SelectIndex from '@/components/Arithmetic/components/SelectIndex';
import _ from 'lodash';

const props = defineProps({
  arithmeticList: {
    type: Array as PropType<any[]>,
    default: () => []
  },
  calculationFormula: {
    type: String,
    default: () => ''
  }
});

/* 指标选择弹出框visible */
const dialogVisible = ref(false);

const popoverRef = ref();
const signRef = ref();

const state = reactive({
  businessMeaning: '',
  popupVisible: false, //指标弹出框
  node: null as any, // 获取到节点
  endIndex: '', // 光标最后停留位置
  position: {
    x: 0,
    y: 0
  }, // 弹窗显示位置
  createBtnIndex: 0 //创建指标下标
});

const ruleRef = ref();
//运算下拉
const symbolList = [
  { label: '+', value: '+' },
  { label: '-', value: '-' },
  { label: '*', value: '*' },
  { label: '/', value: '/' },
  { label: '(', value: '(' },
  { label: ')', value: ')' }
];

//详情处理
const getDetail = () => {
  //计算规则
  const div = document.createElement('div');
  div.setAttribute('data-digitforce-node', 'element');
  getCountRulesHtml(props.arithmeticList, props.calculationFormula, div, 0);
  ruleRef.value.appendChild(div);
  //下标
  state.createBtnIndex = props.arithmeticList.length;
};

//计算规则html处理
const getCountRulesHtml = (
  arithmeticList: any[],
  calculationFormula: string,
  div: HTMLDivElement,
  arrayIndex: number
) => {
  //查找规则中的指标
  let item = arithmeticList[arrayIndex];
  if (item && item.selectIndexName && calculationFormula) {
    //截取指标名称
    let index = calculationFormula.indexOf(item.selectIndexName);
    if (index != -1) {
      let nameIdnex = index + item.selectIndexName.length;
      //按照顺序查找，before没有指标的字符
      let before = calculationFormula.substring(0, index);
      let after = calculationFormula.substring(
        nameIdnex,
        calculationFormula.length
      );
      //指标之前的文本
      if (before) {
        const span = createTextSpanNode();
        span.textContent = before;
        div.appendChild(span);
      }
      //指标文本
      const atButton = createAtButton(item);
      div.appendChild(atButton);
      //有继续查找，没有加入零宽
      console.log(after);
      if (after) {
        getCountRulesHtml(arithmeticList, after, div, arrayIndex + 1);
      } else {
        const span = createSpanNode();
        div.appendChild(span);
      }
    }
  } else {
    //没有指标，直接显示文本
    const span = createTextSpanNode();
    span.textContent = calculationFormula;
    div.appendChild(span);
  }
};

defineExpose({
  ruleRef: ruleRef
});

// 光标位置提示
const rangeIndexInfo = () => {
  //获取编辑对象
  let selection = getSelection();
  if (selection) {
    const range = selection.getRangeAt(0);
    //当前光标位置
    const curIndex = getCursorIndex();
    //当前节点对象
    const curNode: any = getRangeNode();

    //先删除所有提示
    let nodes = document.getElementsByClassName('placeholder-after');
    for (let i = 0; i < nodes.length; i++) {
      nodes[i].classList.remove('placeholder-after');
    }
    //默认插入一行div
    if (ruleRef.value.innerHTML == '') {
      const div = document.createElement('div');
      div.setAttribute('data-digitforce-node', 'element');
      const span = createSpanNode();
      div.appendChild(span);
      ruleRef.value.appendChild(div);
      range?.setStart(span, 0);
      range?.setEnd(span, 0);
    } else {
      //记录节点
      const node = getRangeNode();
      const endIndex: any = getCursorIndex();
      state.node = node;
      state.endIndex = endIndex;
    }
    //如果有换行符，清空
    // if (curNode.innerHTML == '<br>') {
    //   curNode.innerHTML = '\u200b';
    // }

    let contentLen = curNode.textContent.replace(/[\u200b]/g, '');
    //判断光标是否在最后
    if (curIndex == contentLen.length || contentLen.length == '') {
      if (
        curNode.nodeType == 3 &&
        curNode.parentElement &&
        curNode.parentElement.nextSibling == null &&
        curNode.parentElement.contentEditable != 'false'
      ) {
        curNode.parentElement.classList.add('placeholder-after');
      }
      if (curNode.nodeName == 'DIV') {
        curNode.children[0].classList.add('placeholder-after');
      }
      if (curNode.nodeName == 'SPAN') {
        curNode.classList.add('placeholder-after');
      }
    }
  }
};

// 获取光标位置
const getCursorIndex = () => {
  const selection = window.getSelection();
  return selection?.focusOffset; // 选择开始处 focusNode 的偏移量
};
// 获取节点
const getRangeNode = () => {
  const selection = window.getSelection();
  return selection?.focusNode; // 选择的结束节点
};
// 弹窗出现的位置
const getRangeRect = () => {
  const selection = window.getSelection();
  if (selection) {
    const range = selection.getRangeAt(0); // 是用于管理选择范围的通用对象
    const rect = range.getClientRects()[0]; // 择一些文本并将获得所选文本的范围
    return {
      x: rect.x,
      y: rect.y
    };
  }
  return {
    x: 0,
    y: 0
  };
};

//创建span节点
const createSpanNode = (isDelete?: boolean) => {
  //默认零宽，空白标签，便于光标展示
  const childSpan = document.createElement('span');
  childSpan.setAttribute('data-digitforce-zero', isDelete ? 'd' : 'n');
  childSpan.textContent = '\u200b';
  return childSpan;
};
//创建文本节点
const createTextSpanNode = () => {
  const span = document.createElement('span');
  span.setAttribute('data-digitforce-string', 'true');
  return span;
};
//替换零宽节点
const replaceZeroSpanNode = () => {
  let selection = window.getSelection();
  const forceNode = selection?.focusNode;
  let range = selection?.getRangeAt(0);

  const parentElement = forceNode?.parentElement?.parentElement;
  const el: any = forceNode?.parentElement;
  const textContent: any = forceNode?.textContent;
  let isZero = el?.getAttribute('data-digitforce-zero');
  //是零宽就替换成有的
  if (isZero == 'n') {
    let span = createTextSpanNode();
    //替换占位符，并重新赋值
    span.textContent = textContent.replace(/[\u200b]/g, '');
    parentElement?.removeChild(el);
    parentElement?.appendChild(span);
    range?.setStart(span, 1);
    range?.setEnd(span, 1);
  }
  //只剩div元素，要加零宽
  if (
    forceNode?.nodeName == 'DIV' &&
    forceNode?.nodeType == 1 &&
    forceNode.textContent == ''
  ) {
    const span = createSpanNode();
    forceNode.textContent = '';
    forceNode.appendChild(span);
  }
};
//删除节点
const deleteSpanNode = (code: string) => {
  let selection = window.getSelection();
  const forceNode = selection?.focusNode;
  let parentElement: any = null;

  if (forceNode?.nodeType == 3) {
    parentElement = forceNode?.parentElement;
  } else if (forceNode?.nodeType == 1) {
    parentElement = forceNode;
  }

  //判断是否在最后
  if (parentElement?.getAttribute('data-digitforce-zero') == 'n') {
    if (
      (parentElement.className != 'placeholder-after' && code == 'Delete') ||
      code == 'Backspace'
    ) {
      parentElement.parentElement?.removeChild(parentElement);
    } else {
      const span = createSpanNode();
      parentElement.parentElement?.appendChild(span);
    }
  }

  if (
    parentElement?.getAttribute('data-digitforce-string') == 'true' &&
    parentElement.textContent.length == 1
  ) {
    const span = createSpanNode();
    parentElement.parentElement?.appendChild(span);
  }
};

// 是否展示 #
const showAt = () => {
  const node = getRangeNode();
  if (!node || node.nodeType !== Node.TEXT_NODE) return false;
  const content = node.textContent || '';
  const regx = /#([^#\s]*)$/;
  const match = regx.exec(content.slice(0, getCursorIndex()));
  return match && match.length === 2;
};

//是否展示运算符号
const showSign = () => {
  const node = getRangeNode();
  if (!node || node.nodeType !== Node.TEXT_NODE) return false;
  const content = node.textContent || '';
  let str = content.slice(0, getCursorIndex());
  str = str.substring(str.length - 1, str.length);
  let isHas = symbolList.some((item) => item.value == str);
  return isHas;
};

//替换运算符号
const replaceSign = (value: string) => {
  const node = state.node;
  const content = node.textContent || '';
  let index: any = state.endIndex;
  //替换字符
  if (index != undefined) {
    const strAry = content.split('');
    strAry[index - 1] = value;
    node.textContent = strAry.join('');
  }
  // 重置光标的位置
  const range = new Range();
  const selection = window.getSelection();
  range.setStart(node, index);
  range.setEnd(node, index);
  selection?.removeAllRanges();
  selection?.addRange(range);
  //关闭弹出框
  signRef.value.style.display = 'none';
};

// 创建标签
const createAtButton = (item: any) => {
  const btn = document.createElement('span');
  //因子数组的长度作为，设置id，用更新dom节点
  let factorListIndex = state.createBtnIndex;
  btn.id = 'mertric' + factorListIndex;
  btn.style.display = 'inline-block';
  btn.className = 'mertric-btn';
  btn.contentEditable = 'false';
  btn.textContent = `${item.cnName || item.selectIndexName}`;
  btn.addEventListener('mouseover', (e: any) => {
    state.businessMeaning = item.businessMeaning;
    popoverRef.value.style.display = 'block';
    popoverRef.value.style.top =
      e.target.offsetTop - e.target.offsetHeight + 'px';
    popoverRef.value.style.left =
      e.target.offsetLeft + e.target.offsetWidth / 2 + 'px';
  });
  btn.addEventListener('mouseout', () => {
    popoverRef.value.style.display = 'none';
  });
  //增加下标
  state.createBtnIndex = factorListIndex + 1;
  return btn;
};
//替换符号
const replaceString = (raw: any, replacer: any) => {
  return raw.replace(/#([^#\s]*)$/, replacer);
};
// #插入指标
const addMertrics = async (item: any) => {
  const node = state.node;
  const content = ruleRef.value.innerText.replace(/[\u200b]/g, '');

  let nextTextNode: any = null;
  let previousTextNode = null;
  if (
    node &&
    node.parentNode.className != 'mertric-btn' &&
    content != '' &&
    item
  ) {
    const content = node.textContent || '';
    const endIndex = state.endIndex;
    const preSlice = replaceString(content.slice(0, endIndex), '');
    const restSlice = content.slice(endIndex);
    const parentNode =
      node.nodeType == 3 ? node.parentNode.parentNode : node.parentNode;
    const nextNode =
      node.nodeType == 3 ? node.parentNode.nextSibling : node.nextSibling;

    if (preSlice.replace(/[\u200b]/g, '') == '') {
      previousTextNode = createSpanNode();
    } else {
      previousTextNode = createTextSpanNode();
      previousTextNode.textContent = preSlice;
    }
    //没有后面，标识只有前面
    if (restSlice.replace(/[\u200b]/g, '') == '') {
      nextTextNode = createSpanNode();
    } else {
      nextTextNode = createTextSpanNode();
      nextTextNode.textContent = restSlice;
    }
    const atButton = createAtButton(item);
    parentNode.removeChild(node.nodeType == 3 ? node.parentNode : node);
    // 插在文本框中
    if (nextNode) {
      parentNode.insertBefore(previousTextNode, nextNode);
      parentNode.insertBefore(atButton, nextNode);
      parentNode.insertBefore(nextTextNode, nextNode);
    } else {
      parentNode.appendChild(previousTextNode);
      parentNode.appendChild(atButton);
      parentNode.appendChild(nextTextNode);
    }
  } else {
    //第一次没有内容的时候
    if (ruleRef.value.innerHTML == '') {
      const div = document.createElement('div');
      div.setAttribute('data-digitforce-node', 'element');
      const span = createSpanNode();
      div.appendChild(span);
      ruleRef.value.appendChild(div);
    }
    const atButton = createAtButton(item);
    nextTextNode = createSpanNode();
    ruleRef.value.children[0].appendChild(atButton);
    ruleRef.value.children[0].appendChild(nextTextNode);
  }
  // 重置光标的位置
  const range = new Range();
  const selection = window.getSelection();
  range.setStart(nextTextNode, 0);
  range.setEnd(nextTextNode, 0);
  selection?.removeAllRanges();
  selection?.addRange(range);

  rangeIndexInfo();

  //保存指标
  props.arithmeticList.push({
    selectIndexName: item.label,
    selectIndexId: item.id,
    businessMeaning: item.businessMeaning,
    type: item.type,
    subjectId: item.subjectId,
    CombineEnumVal: 'SUM'
  });
};

// 键盘改变事件
const handleChange = (e: any) => {
  //删除时
  if (
    e.inputType === 'deleteContentBackward' ||
    e.inputType === 'deleteContentForward'
  ) {
    for (let i = 0; i < state.createBtnIndex; i++) {
      let id = 'mertric' + i;
      let dom = document.getElementById(id);
      //如果没找到表示已经删除
      if (!dom) {
        if (props.arithmeticList) {
          props.arithmeticList[i] = undefined;
        }
      }
    }
    return false;
  }
  if (showAt()) {
    dialogVisible.value = true;
  } else if (showSign()) {
    state.position = getRangeRect();
    //距离屏幕右侧小于弹出框的宽度,27弹出框宽度，32px偏移
    let left = state.position.x;
    let top = state.position.y + 20;
    if (document.body.clientWidth - left < 27 + 32) {
      left = document.body.clientWidth - 27 - 32;
    }
    if (document.body.clientHeight - top < 156 + 32) {
      top = document.body.clientHeight - 156 - 32;
    }
    signRef.value.style.display = 'block';
    signRef.value.style.left = left + 'px';
    signRef.value.style.top = top + 'px';
  } else {
    dialogVisible.value = false;
    signRef.value.style.display = 'none';
    //更新节点
    replaceZeroSpanNode();
  }
};
// 键盘按下事件
const handleKeyDown = (e: any) => {
  //禁止换行
  if(e.code === 'Enter'){
    e.preventDefault();
  }
  //删除时替换所有的0占位符
  if (e.code === 'Backspace' || e.code === 'Delete') {
    deleteSpanNode(e.code);
  }
};
//焦点离开
const handleBlur = () => {
  const content = ruleRef.value.innerText.replace(/[\u200b]/g, '');
  //焦点离开，内容为空，清空里面的标签
  if (content == '') {
    ruleRef.value.innerHTML = '';
  }
};

const openDialog = () => {
  dialogVisible.value = true;
};

//详情处理
watch(
  () => props.calculationFormula,
  (val) => {
    if (val) {
      nextTick(() => {
        getDetail();
      });
    } else {
      nextTick(() => {
        ruleRef.value.innerHTML == '';
      });
    }
  },
  { immediate: true }
);
</script>
<template>
  <div class="textarea-div">
    <div
      class="rules"
      contenteditable="true"
      placeholder="输入#号可插入指标"
      @keyup="rangeIndexInfo"
      @keydown="handleKeyDown"
      @input="handleChange"
      @click="rangeIndexInfo"
      @blur="handleBlur"
      ref="ruleRef"
    ></div>
    <a-button type="text" @click="openDialog">&lt;/&gt;插入指标</a-button>
    <div ref="popoverRef" class="popover-bus">
      {{ state.businessMeaning }}
      <div class="arrow-down"></div>
    </div>
    <div ref="signRef" class="symbol-list-wrap">
      <div v-for="v in symbolList" :key="v.value" @click="replaceSign(v.value)">
        {{ v.label }}
      </div>
    </div>
    <SelectIndex
      :isOnlyIndex="true"
      :isConfig="false"
      v-model:dialogVisible="dialogVisible"
      @index-change="addMertrics"
    />
  </div>
</template>
<style lang="scss" scoped>
@import './index.scss';
</style>